home *** CD-ROM | disk | FTP | other *** search
/ The Arsenal Files 3 / The Arsenal Files 3.iso / gen_prog / appdemo.exe / MAINLIB.APP < prev    next >
Text File  |  1994-11-07  |  43KB  |  1,448 lines

  1. //                            MAIN A++ LIBRARY
  2. //                            ================
  3.  
  4. // This file contains the main library of functions with which you can build
  5. // your program.  You can modify it to suit your particular needs.  If you
  6. // do then it would be a good idea to lable all your changes with a unique
  7. // string in a comment so that if you get an upgrade you can remake those
  8. // changes.
  9.  
  10. #define FALSE 0
  11. #define TRUE  1
  12.  
  13. // Device (File) access modes
  14. #define READ      0
  15. #define WRITE     1
  16.  
  17. // Devices for Input and Output Streams
  18. #define cin       0  // Can be redirected
  19. #define KEYBOARD  0  // Can be redirected
  20. #define cout      1  // Can be redirected
  21. #define VDU       1  // Can be redirected
  22. #define cerr      2  // Always the VDU
  23. #define AUX       3  // COM1 ?
  24. #define PRN       4  // Normally directed to LPT1
  25. #define LPT1      4
  26. // When files are opened they are allocated device numbers starting
  27. // from 5 by DOS.
  28.  
  29.  
  30. // These global variables contain the command line parameters
  31. // They are initialised in Start()
  32. int argc;           // Number of arguments in the command line
  33. string argv[10];    // The command line parameters
  34.  
  35. #ifdef FILEIO
  36. void Stream()
  37. {
  38.  // This function is to group data together like a "struct" command but faster
  39.  int  BufferSegment[MAXHANDLES];  // Segment where the buffer is stored
  40.  int  BufferPosition[MAXHANDLES]; // Next byte to read from or write to
  41.  int  BufferLength[MAXHANDLES];   // Last byte from which data can be read
  42.  long DOSPosition[MAXHANDLES];    // File Position at which DOS is
  43.  byte Err[MAXHANDLES];            // Error condition of File
  44.  byte ReadWriteType[MAXHANDLES];  // Type of File Access
  45.  byte ColumnNumber[MAXHANDLES];   // Column number used for Tab() function
  46. }
  47. #else
  48. void Stream()
  49. {
  50.  // This function is to group data together like a "struct" command in C++
  51.  byte Err[5];            // Error condition of File
  52.  byte ReadWriteType[5];  // Type of File Access
  53.  byte ColumnNumber[5];   // Column number used for Tab() function
  54. }
  55. #endif
  56.  
  57.  
  58. void Start()
  59. {
  60.  // Start() is where the .EXE program starts.
  61.  // It initialises the program, calls Main() and then End()
  62.  unsigned int PSPAddress;   // PSP segment number
  63.  // NOTE :- The Data Segment hasn't been allocated yet, so no variables
  64.  // in the Data Segment can be accessed yet
  65.  
  66.  // Check that the CPU is a 386 or above by seeing if the IOPL bits can be set
  67.  PUSH FLAGS; POP AX;   // Copy flags into AX
  68.  AX = AX | 3000h;      // Set the IOPL bits
  69.  PUSH AX; POP FLAGS;   // Copy AX back to flags
  70.  PUSH FLAGS; POP AX;   // Copy flags back into AX
  71.  AX = AX & 3000h;      // Isolate the IOPL bits
  72.  if(!AX) // If the IOPL bits have been reset then the CPU is a 286 or lower
  73.  {
  74.   DX = "386 or above required\n$"; // Leave the $ on the end of this for DOS
  75.   DS = SS;             // Set up DS for DOS.  Strings are in the Stack Segment
  76.   AH = 9; INT 21h;     // Use DOS interrupt because the Streams don't work yet
  77.   AX = 4C1Fh; INT 21h; // Terminate program with Error Code 31 (General failure)
  78.  }
  79.  
  80.  // The program is initially allocated all available memory, so release
  81.  // unused memory here. A paragraph is 16 bytes
  82.  BX = SS; AX = ES; BX = BX - AX;    // Paragraphs used by program & PSP
  83.  BX = BX + SP >> 4 + 2;             // Paragraphs used by Stack & Strings
  84.  BX = BX + S2[0Ch] >> 4 + 1;        // Paragraphs used by Data Segment
  85.  SI_ProgramSize = BX;               // Store program size for if TSR program
  86.  // Note ES is still pointing to the PSP
  87.  AH = 4Ah; INT 21h;                 // Resize memory block of this program
  88.  
  89.  // Allocate Address for Data Segment
  90.  AX = SS; AX = AX + SP >> 4 + 2;    // Segment Address of Data Segment
  91.  DS = AX;                           // Whew! Now have a Data Segment
  92.  
  93.  // Can can now use Non String Variables
  94.  
  95.  #ifdef TSR
  96.  // Store size of this program in paragraghs for TSR programs
  97.  unsigned int ProgramSize;
  98.  ProgramSize = SI_ProgramSize;
  99.  TSRActive = FALSE;
  100.  #endif
  101.  
  102.  Error = 0; // Global Error Flag
  103.  PSPAddress = ES;
  104.  End.HasBeenCalledBefore = FALSE;
  105.  
  106.  // Initialise the String Header by copying it from the Stack Segment
  107.  for(SI=0; SI<20h; SI++) D1[SI] = S1[SI];
  108.  
  109.  // Clear all the strings
  110.  AX = StringHeapStartAddress + 2;   // String NULL Address
  111.  DI = StringFirstAddress;           // Initialise DI
  112.  while(DI .<=. StringLastAddress)   // For each String
  113.  {
  114.   S2[DI] = AX;                      // Make it a NULL string
  115.   DI = DI + 2;
  116.  }
  117.  // Initialise the String Heap
  118.  // This looks rather complicated, but is done so that a NULL string can be 
  119.  // treated in the same manner as any other string
  120.  BX = StringHeapStartAddress;
  121.  S1[BX++] = '#'; // Beginning of Heap Marker
  122.  S1[BX++] =  0;  // 
  123.  S1[BX++] =  0;  // String NULL Address. ie The pointer to "" is always to here
  124.  S1[BX++] = '#'; // 
  125.  S1[BX++] =  0;  // 
  126.  S1[BX]   = '#'; // End of Heap Marker. Can start writing string data here
  127.  StringNextFreeAddress = BX;
  128.  
  129.  CopyString.Segment = SS;        // Default segment for strings
  130.  RelocateString.HoleAddress = 0; // There are not any holes in the String Heap yet
  131.  
  132.  // Can now use String Variables as well
  133.  
  134.  #ifdef FILEIO
  135.  for(SI=5; SI<MAXHANDLES; SI++)
  136.  {
  137.   Stream.Err[SI] = 6; // Error number for Invalid Handle
  138.   Stream.ColumnNumber[SI] = 0; 
  139.  }
  140.  #endif
  141.  
  142.  // Initialise Devices
  143.  for(SI=0; SI<5; SI++)
  144.  {
  145.   Stream.Err[SI] = 0;
  146.   Stream.ReadWriteType[SI] = WRITE;
  147.   Stream.ColumnNumber[SI] = 0; 
  148.  }
  149.  Stream.ReadWriteType[0] = READ; // Can only read from keyboard
  150.  
  151.  // Put the complete path and file name of this program in argv[0] 
  152.  PUSH ES;      // Save the PSP address for later
  153.  ES = E2[2Ch]; // Get the environment segment address from the PSP
  154.  for(DI=0; E1[DI] != 0 || E1[DI+1] != 0; DI++) {} // Search for double NULL
  155.  DI = DI + 4;  // move past double NULL and length byte
  156.  CopyString.Segment = ES; // Segment for source string for String Copy
  157.  argv[0] = DI; // Copy the String in
  158.  CopyString.Segment = SS; // Default segment for strings
  159.  POP ES;       // ES = PSP address again
  160.  
  161.  // Put the command line into separate strings argv[1] to argv[argc-1]
  162.  BX = StringNextFreeAddress;
  163.  CL = E1[80h]; CH = 0; CX = CX + 81h; // Final address of command line string
  164.  DI = 1;       // Temporary argc
  165.  SI = 82h;     // Start address of Command line string
  166.  while(SI<CX)
  167.  {
  168.   if(DI >= 10) End(128); // Too many arguments
  169.   while(E1[SI] == ' ') SI++; // Skip past blanks
  170.   argv[DI] == BX;  // Set the string address
  171.   while(E1[SI] != ' ' && E1[SI] != 13) { S1[BX] = E1[SI]; BX++; SI++; } // Copy string
  172.   if(S1[BX-1] == 0) { argv[DI] == ""; DI--; } // if an empty string then point to NULL string
  173.   S1[BX] = 0;     // Terminate the string
  174.   BX++;           // Move past the NULL. BX is still the NextFreeAddress
  175.   DI++;           // Increment argc
  176.   SI++;           // Move past the separator in the command line
  177.  }
  178.  argc = DI;
  179.  S1[BX] = '#';    // End of heap marker
  180.  StringNextFreeAddress = BX;
  181.  //for(SI=0; SI<argc; SI++) cout << argv[SI] << " "; cout << "\n";
  182.  
  183.  #ifdef API
  184.  // Need to have a video mode of 80 X 25 color
  185.  AH = 0Fh; INT 10h; Vdu.Page = BH;    // Get video mode in AL and Page in BH
  186.  if(AL != 3) { AX = 0003h; INT 10h; } // if not video mode 3 then make it 3
  187.  AH = 03h; INT 10h;                   // Get cursor position
  188.  Vdu.Row    = DH;  Vdu.Column = DL;   // Store cursor position
  189.  AH = 08h; INT 10h;                   // Read cursor Attribute in AH
  190.  Vdu.Attribute = AH;
  191.  Vdu.BackgroundColor = AH >> 4;
  192.  Vdu.ForegroundColor = AH & 0Fh;
  193.  Vdu.Blink = 0;
  194.  Vdu.MinCol = 0;
  195.  Vdu.MinRow = 0;
  196.  Vdu.MaxCol = 79;
  197.  Vdu.MaxRow = 24;
  198.  Vdu.CursorOn = 1;
  199.  AX = 0002h; INT 33h;                 // Hide Mouse
  200.  Mouse.Active = 0;
  201.  Mouse.Visible = 0;
  202.  #endif
  203.  
  204.  // Initialise String Structure List
  205.  for(BX=0; BX<MAXSTRUCTURES; BX++) TidyStringHeap.StructureSegment[BX] = 0;
  206.  TidyStringHeap.StructureSegment[0] = SS;
  207.  
  208.  AX = Main();     // Call Main()  ie. Your program
  209.  End(AX);         // Call End() with the return code from your program
  210. }
  211.  
  212.  
  213. void End(AX_Err)
  214. {
  215.  // This Function terminates a program and is called from End()
  216.  // or from your program.  The parameter passed to it is passed on to DOS
  217.  byte HasBeenCalledBefore;
  218.  int Err;
  219.  string Address;
  220.  
  221.  // Prevent infinite loop if there is an error during the End() function 
  222.  if(HasBeenCalledBefore) goto End;
  223.  HasBeenCalledBefore = TRUE;
  224.  
  225.  // These are the most common error messages from APP.CFG
  226.  // You can add more of your own or delete some if you wish
  227.  Err = AX_Err;
  228.  if     (AX_Err == 2) cerr << "File not Found\n";
  229.  else if(AX_Err == 3) cerr << "Path not Found\n";
  230.  else if(AX_Err == 5) cerr << "Access denied\n";
  231.  else if(AX_Err == 6) cerr << "Invalid Handle\n";
  232.  else if(AX_Err == 8) cerr << "Insufficient Memory\n";
  233.  
  234.  if(Err && (Err < 100 || Err >= 200)) // if no compilation error
  235.  {
  236.   //BX = SP;         // Determine Error Address from the Stack.
  237.   //SI = S2[BX];     // Return Address
  238.   //SI = SI - 3;     // CALL Address is 3 bytes before Return Address
  239.   //Address = IntegerToHexString(SI); 
  240.   //cerr << "\nProgram terminated at Address " << Address << " with Error " << Err << "\n\n";
  241.   //SnapShot("ERR.LOG");
  242.   cerr << "\nProgram terminated with Error " << Err << "\n\n";
  243.  }
  244.  
  245.  #ifdef FILEIO
  246.  // Make sure that all files have been closed
  247.  for(SI=5; SI<MAXHANDLES; SI++) if(Stream.Err[SI] != 6) Close(SI);
  248.  #endif
  249.  
  250.  End:
  251.  
  252.  #ifdef TSR
  253.  if(TSRActive)
  254.  {
  255.   GetChar(cin);
  256.   HasBeenCalledBefore = FALSE;
  257.   SP = MainSP; return; // Simulate a return from TSRMain()
  258.  }
  259.  #endif
  260.  
  261.  #ifdef API
  262.  if(Mouse.Visible) HideMouse();
  263.  
  264.  // Clear Screen
  265.  CL = 0;  CH = 0;
  266.  DL = 79; DH = 24;
  267.  BH = 7;
  268.  AX = 0600h; INT 10h; // BIOS scroll up by 0 (Clear Screen)
  269.  
  270.  // Put cursor at top of screen
  271.  BH = 0; DX = 0;
  272.  AH = 2; INT 10h;
  273.  
  274.  /*
  275.  // Move the DOS cursor to the API cursor
  276.  DL = Vdu.Column; DH = Vdu.Row; BH = Vdu.Page;
  277.  AH = 02h; INT 10h;  // Set cursor position
  278.  if(Mouse.Visible) HideMouse();
  279.  */
  280.  #endif
  281.  
  282.  AL = Err;
  283.  AH = 4Ch;
  284.  INT 21h;
  285. }
  286.  
  287.  
  288. unsigned int AllocateMemoryBlock(BX)
  289. {
  290.  // Allocate a block of memory
  291.  // BX is the number of bytes requested
  292.  // Returns the Segment Address
  293.  
  294.  BX = BX >> 4;      // Convert Bytes to 16 byte Paragraphs
  295.  BX = BX + 1;       // Allow for rounding
  296.  AH = 48h; INT 21h; // Call DOS to Allocate memory Block
  297.  if(CARRYFLAG) { Error = AX; return 0; } // If Error
  298.  return AX;
  299. }
  300.  
  301.  
  302. void ReleaseMemoryBlock(AX)
  303. {
  304.  // Release a memory block allocated with AllocateMemoryBlock()
  305.  preserve ES;
  306.  
  307.  // BX is the Segment Address to be released
  308.  ES = AX;
  309.  AH = 49h; INT 21h;     // Call DOS to DeAllocate memory Block
  310.  if(CARRYFLAG) End(AX); // If invalid then Terminate Program
  311. }
  312.  
  313.  
  314. byte InputByte(BX_Handle)
  315. {
  316.  // Inputs a Number from a File or Device and returns it as a Byte
  317.  string TempString;
  318.  
  319.  TryAgain:
  320.  TempString = InputLine(BX_Handle);
  321.  PUSH BX; 
  322.  AX = StringToInteger(TempString);
  323.  POP BX;
  324.  if(BX_Handle == 0 && !StringToInteger.NumberIsValid) goto TryAgain;
  325.  return AL;
  326. }
  327.  
  328.  
  329.  
  330. int InputInteger(BX_Handle)
  331. {
  332.  // Inputs a Number from a File or Device and returns it as an Integer
  333.  string TempString;
  334.  
  335.  TryAgain:
  336.  TempString = InputLine(BX_Handle);
  337.  PUSH BX;
  338.  AX = StringToInteger(TempString);
  339.  POP BX;
  340.  if(BX_Handle == 0 && !StringToInteger.NumberIsValid) goto TryAgain;
  341.  return AX;
  342. }
  343.  
  344.  
  345. long InputLongInteger(BX_Handle)
  346. {
  347.  // Inputs a Number from a File or Device and returns it as a Long Integer
  348.  string TempString;
  349.  
  350.  TryAgain:
  351.  TempString = InputLine(BX_Handle);
  352.  PUSH BX;
  353.  EAX = StringToLongInteger(TempString);
  354.  POP BX;
  355.  if(BX_Handle == 0 && !StringToLongInteger.NumberIsValid) goto TryAgain;
  356.  return EAX;
  357. }
  358.  
  359.  
  360.  
  361. string InputLine(BX_Handle)
  362. {
  363.  // Input a line (Terminated with a Carriage Return) from a file or device 
  364.  int  Handle; 
  365.  string Line;
  366.  byte BufferToppedUp;
  367.  int  BufferSegment;
  368.  int  BufferPosition;
  369.  int  BufferLength;
  370.  byte OldCursorStatus;
  371.  char Character;
  372.  preserve BX, DX, SI, DI, ES;
  373.  
  374.  if(Stream.Err[BX]) { Line = ""; return Line; }
  375.  
  376.  if(BX == 0)
  377.  {
  378.   #ifdef API
  379.   // Move the BIOS cursor to the API cursor
  380.   PUSH BX;
  381.   OldCursorStatus = Vdu.CursorOn; 
  382.   CursorOn();
  383.   POP BX;
  384.   #endif
  385.  
  386.   #ifdef TSR
  387.   // If Device == 0 then read a string from the Keyboard
  388.   cout << "? ";
  389.   DI = ScratchPadAddress;   // The String is copied into the Scratch Pad
  390.   AH = 00h; INT 16h;        // Read the first byte from the keyboard
  391.   while(AL != 13)           // While not a Carriage Return
  392.   {
  393.    if(AL == 0)              // If extended character
  394.    {
  395.     if(AH == 4Bh) AL = 8;   // if Left Arrow replace with Backspace
  396.    }
  397.    if(AL == 8 && DI .>. ScratchPadAddress) // If Backspace and something entered
  398.    {
  399.     PUSH AX;
  400.     cout << "\b \b";        // Move back, delete, move back
  401.     POP AX;
  402.     DI--;
  403.    }
  404.    if(AL > 31 && AL < 127)  // If character valid
  405.    {
  406.     S1[DI] = AL;            // Store the Character
  407.     Character = AL; cout << Character; // Display the Character
  408.     DI++;                   // Point to the next Address
  409.    }
  410.    DL = Vdu.Column; DH = Vdu.Row; BH = Vdu.Page;
  411.    AH = 02h; INT 10h;      // Set cursor position
  412.    AH = 00h; INT 16h;      // Read the first byte from the keyboard
  413.   }
  414.   #else
  415.   // If Device == 0 then read a string from the Keyboard
  416.   cout << "? ";
  417.   DI = ScratchPadAddress;   // The String is copied into the Scratch Pad
  418.   AH = 08h; INT 21h;        // Read the first byte from the keyboard
  419.   while(AL != 13)           // While not a Carriage Return
  420.   {
  421.    if(AL == 0)              // If extended character
  422.    {
  423.     AH = 08h; INT 21h;      // Read extended character
  424.     if(AL == 75) AL = 8;    // if Left Arrow replace with Backspace
  425.     else AL = 0;            // else Ignore
  426.    }
  427.    if(AL == 8 && DI .>. ScratchPadAddress) // If Backspace and something entered
  428.    {
  429.     PUSH AX;
  430.     cout << "\b \b";        // Move back, delete, move back
  431.     POP AX;
  432.     DI--;
  433.    }
  434.    if(AL > 31 && AL < 127)  // If character valid
  435.    {
  436.     S1[DI] = AL;              // Store the Character
  437.     Character = AL; cout << Character; // Display the Character
  438.     DI++;                     // Point to the next Address
  439.    }
  440.    AH = 8; INT 21h;         // Read the next byte from the keyboard
  441.   }
  442.   #endif
  443.   S1[DI] = 0;               // Terminate the String
  444.   Line = ScratchPadAddress; // Copy String from Scratch pad to proper location 
  445.   cout << "\n";
  446.   #ifdef API
  447.   // Restore old cursor state
  448.   if(!OldCursorStatus) CursorOff();
  449.   #endif
  450.   return Line;
  451.  }
  452.  
  453.  
  454.  
  455.  #ifdef FILEIO
  456.  if(BX_Handle >= MAXHANDLES || Stream.Err[BX] == 6) End(6); // If Invalid Handle then terminate program
  457.  if(Stream.ReadWriteType[BX_Handle] != READ) End(5); // Cannot Read from this Device / File
  458.  
  459.  // For speed copy Subscripted variables into Non subscripted variables
  460.  Handle = BX_Handle;
  461.  BufferSegment = Stream.BufferSegment[BX_Handle];
  462.  BufferPosition = Stream.BufferPosition[BX_Handle];
  463.  BufferLength = Stream.BufferLength[BX_Handle];
  464.  
  465.  BufferToppedUp = 0;
  466.  
  467.  // See if there is a Carriage Return in the existing string
  468.  SearchForCR:
  469.  ES = BufferSegment;
  470.  for(SI=BufferPosition; SI < BufferLength; SI++)
  471.  {
  472.   if(E1[SI] == 9) E1[SI] = ' ';    // Replace TAB with SPACE
  473.   if(E1[SI] == 13 || E1[SI] == 26) // CR or EOF
  474.   {
  475.    if(E1[SI] == 26) Stream.Err[BX] = 204;   // Set EOF Flag
  476.    // If this is the last character then we don't know if the next character
  477.    // is a Line Feed or not, so Top up the Buffer
  478.    if(SI+1 == BufferLength && E1[SI] == 13 && BufferToppedUp == 0) break;
  479.    DI_OldSI = SI; DL_OldChar = E1[SI];
  480.    E1[SI] = 0;
  481.    SI++; if(SI < BufferLength && E1[SI] == 10) SI++; // Move past CR, LF
  482.    // Copy the string in
  483.    CopyString.Segment = ES;
  484.    Line = CopyString(BufferPosition);
  485.    CopyString.Segment = SS;
  486.    BufferPosition = SI;
  487.    E1[DI] = DL_OldChar;
  488.  
  489.    // Copy back again
  490.    BX_Handle = Handle;
  491.    Stream.BufferPosition[BX_Handle] = BufferPosition;
  492.    Stream.BufferLength[BX_Handle] = BufferLength;
  493.    //PUSH BX;
  494.    //LogFile << Line << "\n";
  495.    //POP BX;
  496.    return Line;
  497.   }
  498.  }
  499.  if(BufferToppedUp) End(201); // If string too long for buffer then Terminate program
  500.  
  501.  // A carriage Return has not been found so Read in some more data
  502.  // First copy the remaining data back to the start
  503.  BX = 0;
  504.  for(SI=BufferPosition; SI<BufferLength; SI++) { E1[BX] = E1[SI]; BX++; }
  505.  BufferPosition = 0;
  506.  BufferLength = BX;
  507.  // Next Read in the Data
  508.  DX = BX;
  509.  CX = FILEBUFFERSIZE - BX;
  510.  BX = Handle;
  511.  PUSH DS; PUSH BX;
  512.  DS = BufferSegment;
  513.  //Dot();
  514.  AH = 3Fh; INT 21h; // Read the bytes from the device
  515.  POP BX; POP DS;
  516.  if(CARRYFLAG)
  517.  {
  518.   Stream.Err[BX_Handle] = AL; Error = AX;
  519.   Line = "";
  520.   return Line;
  521.  }
  522.  if(AX == 0) // If no bytes read then must be end of File
  523.  {
  524.   Stream.Err[BX] = 204;   // Set File Error Flag
  525.   Line = "";
  526.   return Line;
  527.  }
  528.  BufferLength = BufferLength + AX;
  529.  ECX = 0; CX = AX; Stream.DOSPosition[BX_Handle] = Stream.DOSPosition[BX_Handle] + ECX;
  530.  BufferToppedUp = 1;
  531.  goto SearchForCR;
  532.  return Line; // Prevent Compilation Error
  533.  #endif
  534. }
  535.  
  536.  
  537.  
  538. string InputLineWithDefault(string DefaultString)
  539. {
  540.  // Input a line (Terminated with a Carriage Return) from the keyboard
  541.  // with a default string which can be modified 
  542.  string Line;
  543.  byte BufferToppedUp;
  544.  int  BufferSegment;
  545.  int  BufferPosition;
  546.  int  BufferLength;
  547.  byte OldCursorStatus;
  548.  char Character;
  549.  preserve BX, DX, SI, DI;
  550.  
  551.  #ifdef API
  552.  // Move the BIOS cursor to the API cursor
  553.  OldCursorStatus = Vdu.CursorOn;
  554.  CursorOn();
  555.  #endif
  556.  
  557.  #ifdef TSR
  558.  // If Device == 0 then read a string from the Keyboard
  559.  cout << "? " << DefaultString;
  560.  DI = ScratchPadAddress;   // The String is copied into the Scratch Pad
  561.  SI = 0; while(DefaultString[SI]) S1[DI++] = DefaultString[SI++];
  562.  AH = 00h; INT 16h;        // Read the first byte from the keyboard
  563.  while(AL != 13)           // While not a Carriage Return
  564.  {
  565.   if(AL == 0)              // If extended character
  566.   {
  567.    if(AH == 4Bh) AL = 8;   // if Left Arrow replace with Backspace
  568.   }
  569.   if(AL == 8 && DI .>. ScratchPadAddress) // If Backspace and something entered
  570.   {
  571.    PUSH AX;
  572.    cout << "\b \b";        // Move back, delete, move back
  573.    POP AX;
  574.    DI--;
  575.   }
  576.   if(AL > 31 && AL < 127)  // If character valid
  577.   {
  578.    S1[DI] = AL;            // Store the Character
  579.    Character = AL; cout << Character; // Display the Character
  580.    DI++;                   // Point to the next Address
  581.   }
  582.   DL = Vdu.Column; DH = Vdu.Row; BH = Vdu.Page;
  583.   AH = 02h; INT 10h;       // Set the cursor position
  584.   AH = 00h; INT 16h;       // Read the next byte from the keyboard
  585.  }
  586.  #else
  587.  cout << "? " << DefaultString;
  588.  DI = ScratchPadAddress;   // The String is copied into the Scratch Pad
  589.  // The next line copies the Default String into the scratchpad
  590.  SI = 0; while(DefaultString[SI]) S1[DI++] = DefaultString[SI++];
  591.  AH = 08h; INT 21h;        // Read the first byte from the keyboard
  592.  while(AL != 13)           // While not a Carriage Return
  593.  {
  594.   if(AL == 0)              // If extended character
  595.   {
  596.    AH = 08h; INT 21h;      // Read extended character
  597.    if(AL == 75) AL = 8;    // if Left Arrow replace with Backspace
  598.    else AL = 0;            // else Ignore
  599.   }
  600.   if(AL == 8 && DI .>. ScratchPadAddress) // If Backspace and something entered
  601.   {
  602.    PUSH AX;
  603.    cout << "\b \b";        // Move back, delete, move back
  604.    POP AX;
  605.    DI--;
  606.   }
  607.   if(AL > 31 && AL < 127)  // If character valid
  608.   {
  609.    S1[DI] = AL;            // Store the Character
  610.    Character = AL; cout << Character; // Display the Character
  611.    DI++;                   // Point to the next Address
  612.   }
  613.   AH = 8; INT 21h;         // Read the next byte from the keyboard
  614.  }
  615.  #endif
  616.  S1[DI] = 0;               // Terminate the String
  617.  Line = ScratchPadAddress; // Copy String from Scratch pad to proper location 
  618.  cout << "\n";
  619.  #ifdef API
  620.  // Restore old cursor state
  621.  if(!OldCursorStatus) CursorOff();
  622.  #endif
  623.  return Line;
  624. }
  625.  
  626.  
  627.  
  628. byte AKeyIsWaiting()
  629. {
  630.  // Detects if a key is waiting in the Keyboard buffer
  631.  // Returns 0 if no key has been pressed or 1 if a key has been pressed
  632.  #ifdef TSR
  633.  PUSH ES;
  634.  AX = 0040h; ES = AX;
  635.  if(E2[001Ah] == E2[001Ch]) AL = 0; // Compare keyboard Head and Tail pointers
  636.  else AL = 1;
  637.  POP ES;
  638.  #else
  639.  AH = 0Bh; INT 21h;
  640.  AL = AL & 1;  // Change 0xFF to 1
  641.  #endif
  642.  return AL;
  643. }
  644.  
  645.  
  646. char GetChar(BX_Handle)
  647. {
  648.  // Input a character from a file or device
  649.  AL = GetByte(BX_Handle);
  650.  return AL;
  651. }
  652.  
  653.  
  654.  
  655. byte GetByte(BX_Handle)
  656. {
  657.  // Input a Binary byte from a file or device 
  658.  preserve BX, DX, DI, ES;
  659.  
  660.  DI_Handle = BX_Handle;
  661.  if(BX == 0)
  662.  {
  663.   // If Device == 0 then read a string from the Keyboard
  664.   #ifdef TSR
  665.   // TSR's cannot use DOS function 08h, so use BIOS instead
  666.   AL = 00h;
  667.   while(AL == 0)            // While no valid character read
  668.   {
  669.    DL = Vdu.Column; DH = Vdu.Row; BH = Vdu.Page;
  670.    AH = 02h; INT 10h;  // Set cursor position
  671.  
  672.    AH = 00h; INT 16h;       // Read the next byte from the keyboard
  673.   }
  674.   #else
  675.   // Use DOS function to read key rather than BIOS so that
  676.   // input can be redirected
  677.   AH = 08h; INT 21h;        // Read the first byte from the keyboard
  678.   while(AL == 0)            // While an extended character
  679.   {
  680.    AH = 08h; INT 21h;       // Read extended character
  681.    AH = 08h; INT 21h;       // Read the next byte from the keyboard
  682.   }
  683.   #endif
  684.   return AL;
  685.  }
  686.   
  687.  #ifdef FILEIO
  688.  if(DI_Handle >= MAXHANDLES || Stream.Err[DI_Handle] == 6) End(6); // If Invalid Handle then terminate program
  689.  if(Stream.ReadWriteType[DI_Handle] != READ) End(5); // Cannot Read from this Device / File
  690.  ES = Stream.BufferSegment[DI_Handle];
  691.  
  692.  //LogFile << "Handle   = " << DI_Handle << "\n";
  693.  //LogFile << "Segment  = " << Stream.BufferSegment[DI_Handle] << "\n";
  694.  //LogFile << "Position = " << Stream.BufferPosition[DI_Handle] << "\n";
  695.  //LogFile << "Length   = " << Stream.BufferLength[DI_Handle] << "\n";
  696.  if(Stream.BufferPosition[DI_Handle] >= Stream.BufferLength[DI_Handle])
  697.  {
  698.   // There are not any more characters in the buffer, so read some more in
  699.   DX = 0;
  700.   CX = FILEBUFFERSIZE;
  701.   BX = DI_Handle;
  702.   PUSH DS;
  703.   DS = Stream.BufferSegment[DI_Handle];
  704.   //Dot();
  705.   AH = 3Fh; INT 21h; // Read the bytes from the device
  706.   POP  DS;
  707.   if(CARRYFLAG)
  708.   {
  709.    Stream.Err[DI_Handle] = AL; Error = AX;
  710.    return 0;
  711.   }
  712.   if(AX == 0) // If no bytes read then must be end of File
  713.   {
  714.    Stream.Err[DI_Handle] = 204;   // Set File Error Flag
  715.    return 0;
  716.   }
  717.   Stream.BufferPosition[DI_Handle] = 0;
  718.   Stream.BufferLength[DI_Handle] = AX;
  719.   ECX = 0; CX = AX; Stream.DOSPosition[DI_Handle] = Stream.DOSPosition[DI_Handle] + ECX;
  720.  } 
  721.  
  722.  BX_Handle = DI_Handle; // Now use BX to store the Handle
  723.  DI = Stream.BufferPosition[BX_Handle];
  724.  Stream.BufferPosition[BX_Handle]++;
  725.  //LogFile << "char[0]  = " << E1[DI] << "\n";
  726.  return E1[DI];
  727.  
  728.  #else
  729.  End(5); // Invalid Handle
  730.  return 0;
  731.  #endif
  732. }
  733.  
  734.  
  735.  
  736. int GetInt(BX_Handle)
  737. {
  738.  // Input a binary integer from a file or device
  739.  char LowerByte;
  740.  
  741.  LowerByte = GetChar(BX_Handle);
  742.  AH = GetChar(BX_Handle);
  743.  AL = LowerByte;
  744.  return AX;
  745. }
  746.  
  747.  
  748.  
  749. long GetLong(BX_Handle)
  750. {
  751.  // Input a binary long integer from a file or device
  752.  char LowerByte;
  753.  int  LowerWord;
  754.  
  755.  LowerByte = GetChar(BX_Handle);
  756.  AH = GetChar(BX_Handle);
  757.  AL = LowerByte;
  758.  LowerWord = AX;
  759.  LowerByte = GetChar(BX_Handle);
  760.  EAX = 0;
  761.  AH = GetChar(BX_Handle);
  762.  AL = LowerByte;
  763.  EAX = EAX << 16;
  764.  AX = LowerWord;
  765.  return EAX;
  766. }
  767.  
  768.  
  769.  
  770. void PrintChar(BX_Handle, AL)
  771. {
  772.  // Print one character
  773.  PutByte(BX_Handle, AL);
  774. }
  775.  
  776.  
  777.  
  778. void PrintByte(BX_Handle, AL)
  779. {
  780.  // Print a signed byte as a number
  781.  AX = SIGNEXTEND(AL);
  782.  PrintInteger(BX_Handle, AX);
  783. }
  784.  
  785.  
  786. void PrintUnsignedByte(BX_Handle, AL)
  787. {
  788.  // Print an unsigned byte as a number
  789.  AH = 0;
  790.  PrintUnsignedInteger(BX_Handle, AX);
  791. }
  792.  
  793.  
  794. void PrintInteger(BX_Handle, CX)
  795. {
  796.  // Print a signed integer as a number
  797.  byte NegativeNumber;
  798.  string PrtString;
  799.  preserve DX, DI;
  800.  
  801.  PUSH BX;
  802.  PrtString = IntegerToString(CX); 
  803.  POP BX;
  804.  PrintString(BX, PrtString);
  805. }
  806.  
  807.  
  808.  
  809. void PrintUnsignedInteger(BX_Handle, AX)
  810. {
  811.  // Print an unsigned integer as a number
  812.  preserve DX, DI;
  813.  
  814.  DI = ScratchPadAddress + 30;
  815.  S1[DI] = 0;
  816.  CX = 10;
  817.  if(AX == 0) { DI--; S1[DI] = '0'; }
  818.  while(AX .>. 0)       // Unsigned compare
  819.  {
  820.   DX = 0;
  821.   AX = DX:AX ./. CX; // DX = Remainder of Unsigned Divide
  822.   DL = DL + 48;
  823.   DI--;
  824.   S1[DI] = DL;
  825.  }
  826.  PrintString(BX_Handle, DI);
  827. }
  828.  
  829.  
  830.  
  831. void PrintLongInteger(BX_Handle, EAX_Value)
  832. {
  833.  // Print a signed long integer as a number
  834.  byte NegativeNumber;
  835.  preserve EDX, DI;
  836.  
  837.  DI = ScratchPadAddress + 30;
  838.  S1[DI] = 0;
  839.  ECX = 10;
  840.  if(EAX == 0) { DI--; S1[DI] = '0'; }
  841.  NegativeNumber = 0;
  842.  if(EAX < 0) { EAX = - EAX; NegativeNumber = 1; }
  843.  while(EAX > 0)
  844.  {
  845.   EDX = 0;
  846.   EAX = EDX:EAX / ECX; // EDX = Remainder
  847.   DL = DL + 48;
  848.   DI--;
  849.   S1[DI] = DL;
  850.  }
  851.  if(NegativeNumber) { DI--; S1[DI] = '-'; }
  852.  PrintString(BX_Handle, DI);
  853. }
  854.  
  855.  
  856.  
  857.  
  858. void PrintString(BX_Handle, AX_StringToPrint)
  859. {
  860.  // Print a string
  861.  int Handle;
  862.  unsigned int  Len;
  863.  unsigned int  BufferPosition;
  864.  unsigned int  BufferSegment;
  865.  preserve ALL, ES;
  866.  
  867.  BP_StringToPrint = AX_StringToPrint;
  868.  Handle = BX_Handle;
  869.  Len = StringLength(BP_StringToPrint);
  870.  if(Len == 0) return;
  871.  BX = Handle; 
  872.  if(BX_Handle < 5)
  873.  {
  874.   #ifdef API
  875.   if(BX_Handle < 3)
  876.   {
  877.    if(Mouse.Visible)
  878.    {
  879.     HideMouse();
  880.     while(S1[BP]) PutByte(BX, S1[BP++]);
  881.     ShowMouse();
  882.    }
  883.    else while(S1[BP]) PutByte(BX, S1[BP++]);
  884.    return;
  885.   }
  886.   #endif
  887.  
  888.   // If a hardware device then write it out straight away
  889.   CX = Len; DX = BP;
  890.   PUSH DS; DS = SS; AH = 40h; INT 21h; POP DS; // Write data
  891.   Stream.ColumnNumber[BX_Handle] = Stream.ColumnNumber[BX_Handle] + Len;
  892.   SI = BP + Len; // Point to end of string
  893.   if(S1[SI-1] == 13 || S1[SI-2] == 13) Stream.ColumnNumber[BX_Handle] = 0;
  894.   return;
  895.  }
  896.  
  897.  #ifdef FILEIO
  898.  BX = Handle;
  899.  if(Stream.ReadWriteType[BX_Handle] != WRITE) End(5);
  900.  BufferPosition = Stream.BufferPosition[BX_Handle];
  901.  BufferSegment = Stream.BufferSegment[BX_Handle];
  902.  if(BufferPosition + Len .>=. FILEBUFFERSIZE)
  903.  {
  904.   Flush(Handle);
  905.   BufferPosition = 0;
  906.   if(Len .>=. FILEBUFFERSIZE) End(201); // If too Long then Terminate Program
  907.  }
  908.  // Copy into Buffer
  909.  ES = BufferSegment;
  910.  SI = BP_StringToPrint;
  911.  DI = BufferPosition;
  912.  while(S1[SI] != 0) { E1[DI] = S1[SI]; SI++; DI++; }
  913.  BX_Handle = Handle;
  914.  Stream.BufferPosition[BX_Handle] = BufferPosition + Len;
  915.  Stream.ColumnNumber[BX_Handle] = Stream.ColumnNumber[BX_Handle] + Len;
  916.  if(S1[SI-1] == 13 || S1[SI-2] == 13) Stream.ColumnNumber[BX_Handle] = 0;
  917.  #endif
  918. }
  919.  
  920.  
  921. void PutByte(BX_Handle, CL_Byte)
  922. {
  923.  // Write a byte in binary form
  924.  byte Byte;
  925.  byte PrintableChar;
  926.  preserve DX, DI, ES;
  927.  
  928.  if(BX_Handle < 5)
  929.  {
  930.   Byte = CL;
  931.   #ifdef API
  932.   if(BX_Handle < 3)
  933.   {
  934.    AL = Vdu.Page; AH = 0; AX = AX * 100h + B800h; ES = AX; // Page address
  935.    Vdu.Attribute = ( Vdu.ForegroundColor + Vdu.BackgroundColor << 4 ) | Vdu.Blink;
  936.    PrintableChar = TRUE;
  937.    if(CL == 7 ) { Beep();    PrintableChar = FALSE; }      // Bell
  938.    if(CL == 8 ) { Vdu.Column--; PrintableChar = FALSE; }   // Back Space
  939.    if(CL == 10) { Vdu.Row++; PrintableChar = FALSE; }      // Line Feed
  940.    if(CL == 13) { Vdu.Column = Vdu.MinCol; PrintableChar = FALSE; } // Carriage Return
  941.    if(Vdu.Row > Vdu.MaxRow)
  942.    {
  943.     Vdu.Row--;
  944.  
  945.     /*
  946.     // Scroll screen up
  947.     for(DI=0; DI<3840; DI=DI+4) E4[DI] = E4[DI+160];
  948.     // Clear the bottom line
  949.     for(DI=3840; DI<4000; DI=DI+2) { E1[DI] = ' '; E1[DI+1] = Vdu.Attribute; }
  950.     */
  951.  
  952.     CL = Vdu.MinCol;
  953.     CH = Vdu.MinRow;
  954.     DL = Vdu.MaxCol;
  955.     DH = Vdu.MaxRow;
  956.     BH = Vdu.Attribute;
  957.     if(Mouse.Visible)
  958.     {
  959.      HideMouse();
  960.      AX = 0601h; INT 10h; // BIOS scroll up
  961.      ShowMouse();
  962.     }
  963.     else { AX = 0601h; INT 10h; } // BIOS scroll up
  964.    }
  965.    if(Vdu.Column < Vdu.MinCol) return;
  966.    if(Vdu.Row    < Vdu.MinRow) return;
  967.    if(Vdu.Column > Vdu.MaxCol) return;
  968.    if(Vdu.Row    > Vdu.MaxRow) return;
  969.  
  970.    if(PrintableChar)
  971.    {
  972.     // Calculate the character position on the screen
  973.     AL = Vdu.Column; AH = 0;
  974.     DI = AX;
  975.     AL = Vdu.Row;
  976.     DI = (DI + AX * 80) * 2;
  977.  
  978.     // Write the character
  979.     if(Mouse.Visible)
  980.     {
  981.      HideMouse();
  982.      E1[DI] = Byte;
  983.      E1[DI+1] = Vdu.Attribute;
  984.      ShowMouse();
  985.     }
  986.     else
  987.     {
  988.      E1[DI] = Byte;
  989.      E1[DI+1] = Vdu.Attribute;
  990.     }
  991.     Vdu.Column++;
  992.    }
  993.    Stream.ColumnNumber[cout] = Vdu.Column;
  994.    Stream.ColumnNumber[cerr] = Vdu.Column;
  995.    if(Vdu.CursorOn)
  996.    {
  997.     // Update BIOS cursor position
  998.     DL = Vdu.Column; DH = Vdu.Row; BH = Vdu.Page;
  999.     AH = 02h; INT 10h;  // Set cursor position
  1000.    }
  1001.    return;
  1002.   }
  1003.   #endif
  1004.   // If a hardware device then write it out straight away
  1005.   CX = 1; AX = ADDRESSOF(Byte); DX = AX;
  1006.   AH = 40h; INT 21h; // Write data
  1007.   Stream.ColumnNumber[BX_Handle]++;
  1008.   if(Byte == 13) Stream.ColumnNumber[BX_Handle] = 0;
  1009.   return;
  1010.  }
  1011.  #ifdef FILEIO
  1012.  if(Stream.ReadWriteType[BX_Handle] != WRITE) End(5);
  1013.  if(Stream.BufferPosition[BX_Handle] + 1 >= FILEBUFFERSIZE) { PUSH CX; Flush(BX_Handle); POP CX;}
  1014.  ES = Stream.BufferSegment[BX_Handle];
  1015.  DI = Stream.BufferPosition[BX_Handle];
  1016.  E1[DI] = CL_Byte;
  1017.  Stream.BufferPosition[BX_Handle]++;
  1018.  Stream.ColumnNumber[BX_Handle]++;
  1019.  if(CL_Byte == 13) Stream.ColumnNumber[BX_Handle] = 0;
  1020.  #endif
  1021. }
  1022.  
  1023.  
  1024. void PutInt(BX_Handle, CX_Int)
  1025. {
  1026.  // Write an integer in binary form
  1027.  PutByte(BX_Handle, CL);
  1028.  PutByte(BX_Handle, CH);
  1029. }
  1030.  
  1031.  
  1032. void PutLong(BX_Handle, ECX)
  1033. {
  1034.  // Write a long integer in binary form
  1035.  preserve EDX;
  1036.  
  1037.  EDX = ECX << 16;
  1038.  PutByte(BX_Handle, CL);
  1039.  PutByte(BX_Handle, CH);
  1040.  PutByte(BX_Handle, DL);
  1041.  PutByte(BX_Handle, DH);
  1042. }
  1043.  
  1044.  
  1045. void Tab(BX_Handle, CL)
  1046. {
  1047.  // Move to a column number by filling in with spaces
  1048.  byte TabPosition;
  1049.  preserve BX;
  1050.  
  1051.  TabPosition = CL - 1;
  1052.  while(Stream.ColumnNumber[BX] < TabPosition) PrintChar(BX, ' ');
  1053. }
  1054.  
  1055.  
  1056. int DayOfWeek(int Day, int Month, int Year)
  1057. {
  1058.  // Calculates the day of the week given the date
  1059.  // The year must be the Full year. ie. 1994 and not 94
  1060.  // Returns 0 = Sunday, 1 = Monday,  ... 6 = Saturday
  1061.  preserve EDX;
  1062.  
  1063.  // To make Leap years work correctly make each year start from 1st March
  1064.  if(Month < 3) { Month = Month + 12; Year--; }
  1065.  
  1066.  // Calculate number of Days in Years as :-   Years * 365.25
  1067.  EBX = 0; BX = Year;      // Convert Year from 16 to 32 bit
  1068.  EAX = EBX_Year * 36525;  // Days since year Dot Times 100
  1069.  EDX = 0; EBX = 100;      // Prepare registers for 64 bit divide
  1070.  DIV EBX;                 // Unsigned Divide EDX:EAX by EBX (100)
  1071.  ECX_DayNumber = EAX;     // Days since year Dot
  1072.  
  1073.  // Calculate number of Days in Months as :-    (Months + 1) * 30.6
  1074.  EBX = 0; BX = Month;     // Convert Month from 16 to 32 bit
  1075.  EAX = (EBX_Month + 1) * 306; // 30.6 days per month
  1076.  EDX = 0; EBX = 10;       // Prepare registers for 64 bit divide
  1077.  DIV EBX;                 // Unsigned Divide by 10
  1078.  ECX_DayNumber = ECX_DayNumber + EAX; // Add days in months to days in years
  1079.  
  1080.  // Add Days
  1081.  EBX = 0; BX = Day;       // Convert Day of Month from 16 to 32 bit
  1082.  ECX_DayNumber = ECX_DayNumber + EBX;  // Add Days 
  1083.  
  1084.  // Every 100 years a leap year is skipped
  1085.  EAX = 0; AX = Year;      // Convert Year from 16 to 32 bit
  1086.  EDX = 0; EBX = 100;      // Prepare registers for 64 bit divide
  1087.  DIV EBX;                 // Unsigned Divide Year by 100
  1088.  ECX_DayNumber = ECX_DayNumber - EAX; // Subtract days skipped
  1089.  
  1090.  // Every 400 years a leap year is added
  1091.  EAX = 0; AX = Year;      // Convert Year from 16 to 32 bit
  1092.  EDX = 0; EBX = 400;      // Prepare registers for 64 bit divide
  1093.  DIV EBX;                 // Unsigned Divide Year by 400
  1094.  ECX_DayNumber = ECX_DayNumber + EAX; // Add Extra days
  1095.  
  1096.  // Calculate the remainder of a divide by 7
  1097.  EAX = ECX_DayNumber - 1; // Subtract a day so that days begin on Sunday
  1098.  EDX = 0; EBX = 7;        // Prepare registers for 64 bit divide
  1099.  DIV EBX;                 // Unsigned Divide by 7. EDX contains remainder
  1100.  return DX;               // 16 bit version of EDX has remainder
  1101.  // The Day number returned in ECX can be used to calculate the number
  1102.  // of days between two dates.
  1103. }
  1104.  
  1105.  
  1106.  
  1107. void SnapShot(string SnapShotFileExtension)
  1108. {
  1109.  // This can be used like a flight recorder to determine what state the program
  1110.  // was in when an error occured, or you can use it to take snap shots of the 
  1111.  // program at various places to find an elusive bug
  1112.  string CommandLine;
  1113.  string ThisProgName;
  1114.  
  1115.  // Get the Current File Name
  1116.  SI = 0; while(argv[0][SI] != '.') SI++; // Search for '.'
  1117.  while(argv[0][SI] != '\') SI--; // Search back for '\'
  1118.  DI = ScratchPadAddress; SI++;
  1119.  while(argv[0][SI] != '.') S1[DI++] = argv[0][SI++]; // Copy String in
  1120.  S1[DI] = 0;
  1121.  ThisProgName = ScratchPadAddress;
  1122.  
  1123.  // Note you must supply the full path of file SNAPSHOT here
  1124.  CommandLine = "C:\APP\SNAPSHOT.EXE " + ThisProgName
  1125.               + "." + SnapShotFileExtension
  1126.               + " " + IntegerToHexString(SS)
  1127.               + " " + IntegerToHexString(SP)
  1128.               + " " + IntegerToHexString(DS)
  1129.               + " " + IntegerToHexString(CS);
  1130.  //cout << CommandLine << "\n";
  1131.  RunChildProgram(CommandLine);
  1132. }
  1133.  
  1134.  
  1135.  
  1136. int Shell(string CommandLine)
  1137. {
  1138.  // Run a DOS command, or make a DOS prompt if "" is enterd for the Command
  1139.  string COMSPECString;
  1140.  preserve DI, ES;
  1141.  
  1142.  ES = Start.PSPAddress;
  1143.  ES = E2[2Ch]; // Get the environment segment address from the PSP
  1144.  DI = 0;
  1145.  while(AX == AX) // Forever
  1146.  {
  1147.   // Search for "COMSPEC"
  1148.   if    (E1[DI] == 'C'
  1149.     && E1[DI+1] == 'O'
  1150.     && E1[DI+2] == 'M'
  1151.     && E1[DI+3] == 'S'
  1152.     && E1[DI+4] == 'P'
  1153.     && E1[DI+5] == 'E'
  1154.     && E1[DI+6] == 'C') break;
  1155.   DI++;
  1156.  }
  1157.  DI = DI + 8;  // move past "COMSPEC="
  1158.  CopyString.Segment = ES; // Segment for source string for String Copy
  1159.  COMSPECString = DI;      // Copy the String in
  1160.  CopyString.Segment = SS; // Default segment for strings
  1161.  
  1162.  if(CommandLine[0]) COMSPECString = COMSPECString + " /C " + CommandLine;
  1163.  //cout << COMSPECString << "\n";
  1164.  AX = RunChildProgram(COMSPECString);
  1165.  return AX;
  1166. }
  1167.  
  1168.  
  1169.  
  1170. int RunChildProgram(string CommandLine)
  1171. {
  1172.  // Runs the DOS program specified by CommandLine
  1173.  // This is a faster but more fussy version of Shell() for when you
  1174.  // want to run another .EXE or .COM file
  1175.  
  1176.  // Returns Error code (0 if No Error)
  1177.  // The CommandLine must contain the name of the Executable file 
  1178.  // including the Extension. eg. "MIRROR.EXE"
  1179.  // If the file is not in the current directory the full path must be given
  1180.  // eg. "C:\TEST\MIRROR.EXE"
  1181.  // Parameters can be put on the end. eg. "MIRROR.EXE /1"
  1182.  // This function is documented in DOS technical manuals as the EXEC function
  1183.  
  1184.  preserve DI,SI,DX,ES;
  1185.  
  1186.  //cout << CommandLine << "\n";
  1187.  
  1188.  // Copy first part of CommandLine into ScratchPad starting at 0x80
  1189.  BX = ScratchPadAddress + 0x80;
  1190.  SI = CommandLine;
  1191.  while(S1[SI] && S1[SI] != ' ') S1[BX++] = S1[SI++];
  1192.  S1[BX++] = 0;
  1193.  
  1194.  // Copy remainder of CommandLine into ScratchPad
  1195.  DI_CommandLineAddress = BX;
  1196.  BX++; // Leave room for Char Count Byte
  1197.  S1[BX++] = ' ';        // A space is required at the start of the Tail for reliable operation
  1198.  if(S1[SI]) SI++;       // Skip past space in Command Line if there is one
  1199.  CL_CharCount = 1;      // Count the characters in the Tail. Already have 1
  1200.  while(S1[SI]) { S1[BX++] = S1[SI++]; CL_CharCount++; } // Copy Tail in
  1201.  S1[BX++] = 13; S1[BX++] = 0; // Terminate the Tail
  1202.  S1[DI] = CL;           // Store the Tail length
  1203.  
  1204.  //PrintScratchPad();
  1205.  
  1206.  // Set up the parameter block in the ScratchPad at address 0x100
  1207.  BX = ScratchPadAddress + 0x100;
  1208.  ES = Start.PSPAddress;
  1209.  S2[BX]    = E2[2Ch]; // Environment Address
  1210.  //S2[BX]    = 0;       // Use default Environment Address
  1211.  S2[BX+2]  = DI_CommandLineAddress;     S2[BX+4]  = SS; // Command Tail (Command Line Parameters)
  1212.  S2[BX+6]  = ScratchPadAddress + 0x120; S2[BX+8]  = SS; // Default FCB 1 Address. See below
  1213.  S2[BX+10] = ScratchPadAddress + 0x120; S2[BX+12] = SS; // Default FCB 2 Address. See below
  1214.  
  1215.  // Put Default FCB Data in ScratchPad at address 0x120
  1216.  S1[BX+0x120] = 0;
  1217.  for(SI=0x121; SI<0x12C; SI++) S1[BX+SI] = ' ';
  1218.  for(SI=0x12C; SI<0x145; SI++) S1[BX+SI] = 0;
  1219.  
  1220.  //PUSH ALL; cout << "Command = "; PrintString(1, ScratchPadAddress + 0x80); cout << "\n"; POP ALL;
  1221.  //PUSH ALL; cout << "Tail    = "; PrintString(1, DI + 1); cout << "\n"; POP ALL;
  1222.  
  1223.  // Set up the pointers and call the child program
  1224.  ES = SS;                     // ES:BX points to parameter block. BX already set
  1225.  PUSH DS;                     // Preserve DS
  1226.  DX = ScratchPadAddress + 0x80; DS = SS; // DS:DX points to Command Line 
  1227.  AX = 4B00h; INT 21h;         // Call the Child Program
  1228.  POP DS;                      // Restore DS
  1229.  if(CARRYFLAG) { Error = AX; return AX; }
  1230.  return 0;
  1231. }
  1232.  
  1233.  
  1234. void Beep()
  1235. {
  1236.  // Makes a Beep
  1237.  preserve DX;
  1238.  
  1239.  #ifdef TSR
  1240.  BX = ScratchPadAddress;
  1241.  S1[BX] = 7; // Bell
  1242.  BX = 1; CX = 1; DX = ScratchPadAddress;
  1243.  PUSH DS; DS = SS; AH = 40h; INT 21h; POP DS; // Write character
  1244.  #else
  1245.  DL = 7; AH = 2; INT 21h; // print "BELL"
  1246.  #endif
  1247. }
  1248.  
  1249.  
  1250. string GetTime()
  1251. {
  1252.  // Returns a string containing the Time
  1253.  string Time;
  1254.  preserve DX;
  1255.  
  1256.  AH = 2Ch; INT 21h;  // Get Time
  1257.  //         CH = Hours
  1258.  //         CL = Minutes
  1259.  //         DH = Seconds
  1260.  //         DL = Hundredths of a Second
  1261.  
  1262.  DL = CL; 
  1263.  Time = ByteToString(CH) + ":";
  1264.  if(DL < 10) Time = Time + "0";
  1265.  Time = Time + ByteToString(DL) + ":";
  1266.  if(DH < 10) Time = Time + "0";
  1267.  Time = Time + ByteToString(DH);
  1268.  return Time;
  1269. }
  1270.  
  1271.  
  1272. string GetDate()
  1273. {
  1274.  // Returns a string containing the Date
  1275.  string Date;
  1276.  preserve DX,DI;
  1277.  
  1278.  DX = ScratchPadAddress; 
  1279.  PUSH DS; DS = SS; AX = 3800h; INT 21h; POP DS; // Get country info
  1280.  BX = ScratchPadAddress; DI_DateFormat = S2[BX];
  1281.  if(CARRYFLAG) DI_DateFormat = 0; // if no date information use US format
  1282.  
  1283.  AH = 2Ah; INT 21h;  // Get Date
  1284.  // returns AL = Day of week , 0 = Sunday
  1285.  //         CX = Year
  1286.  //         DH = Month
  1287.  //         DL = Day 
  1288.  
  1289.  if(DI_DateFormat == 0)
  1290.  {
  1291.   DI = CX;
  1292.   Date = ByteToString(DH) + "-" + ByteToString(DL) + "-" + IntegerToString(DI);
  1293.  }
  1294.  else if(DI_DateFormat == 1)
  1295.  {
  1296.   DI = CX;
  1297.   Date = ByteToString(DL) + "-" + ByteToString(DH) + "-" + IntegerToString(DI);
  1298.  }
  1299.  else
  1300.  {
  1301.   DI = CX;
  1302.   Date = IntegerToString(DI) + "-" + ByteToString(DH) + "-" + ByteToString(DL);
  1303.  }
  1304.  return Date;
  1305. }
  1306.  
  1307.  
  1308. void Delay(BX_Delay)
  1309. {
  1310.  // Delays the program for the Delay specified in Hundredths of a second
  1311.  preserve ESI, EDI;
  1312.  
  1313.  ESI = 0; SI = BX;                // Save delay time in 32 bit integer
  1314.  AH = 2Ch; INT 21h;               // Get current time
  1315.  // Calculate initial time in hundreths of a second
  1316.  EBX = 0;                         // Running total
  1317.  EAX = 0;                         // Temporary Variable
  1318.  BL = CH;                         // EBX = Total Hours
  1319.  AL = CL; EBX = EBX * 60 + EAX;   // EBX = Total Minutes
  1320.  AL = DH; EBX = EBX * 60 + EAX;   // EBX = Total Seconds
  1321.  AL = DL; EBX = EBX * 100 + EAX;  // EBX = Total Hundreths of a second
  1322.  ESI_FinishTime = ESI + EBX;      // Calculate finish time
  1323.  while(EBX_CurrentTime < ESI_FinishTime) // while time not up
  1324.  {
  1325.   EDI_PreviousTime = EBX_CurrentTime; // Used to check for going past midnight
  1326.   AH = 2Ch; INT 21h;               // Get current time
  1327.   // Calculate time in hundreths of a second
  1328.   EBX = 0;                         // Running total
  1329.   EAX = 0;                         // Temporary Variable
  1330.   BL = CH;                         // EBX = Total Hours
  1331.   AL = CL; EBX = EBX * 60 + EAX;   // EBX = Total Minutes
  1332.   AL = DH; EBX = EBX * 60 + EAX;   // EBX = Total Seconds
  1333.   AL = DL; EBX = EBX * 100 + EAX;  // EBX = Total Hundreths of a second
  1334.   if(EBX_CurrentTime < EDI_PreviousTime) EBX = EBX + 8640000; // Gone past midnight
  1335.  }
  1336. }
  1337.  
  1338.  
  1339. string GetCurrentDirectory()
  1340. {
  1341.  // Returns a string containing the Current Directory
  1342.  string Directory;
  1343.  preserve SI;
  1344.  
  1345.  PUSH DS; DS = SS; SI = ScratchPadAddress;
  1346.  DL = 0;  // For Current Drive
  1347.  AH = 47h; INT 21h;
  1348.  AX = DS; POP DS;
  1349.  Directory = "\\" + ScratchPadAddress; // Note that "\\" is really "\". See notes on string constants
  1350.  return Directory;
  1351. }
  1352.  
  1353.  
  1354. string GetCurrentDrive()
  1355. {
  1356.  // Returns a string containing the Current Drive
  1357.  string Drive;
  1358.  preserve SI;
  1359.  
  1360.  Drive = "A:";
  1361.  AH = 19h; INT 21h;
  1362.  Drive[0] = Drive[0] + AL;
  1363.  return Drive;
  1364. }
  1365.  
  1366.  
  1367. unsigned int CreateNewStructure(AX_StringFirstAddress, CX_StringLastAddress, BX_Size)
  1368. {
  1369.  preserve DX, ES;
  1370.  
  1371.  PUSH AX; PUSH CX;
  1372.  DX_Segment = AllocateMemoryBlock(BX_Size);
  1373.  POP CX; POP AX;
  1374.  if(DX_Segment == 0) return DX_Segment; // Not enough memory
  1375.  ES = DX_Segment;
  1376.  E2[00h] = AX;
  1377.  E2[02h] = CX;
  1378.  if(CX_StringLastAddress >= AX_StringFirstAddress)
  1379.  {
  1380.   // if the structure has strings then store the Structure address for TidyStringHeap
  1381.   CL_Found = 0;
  1382.   for(BX=0; BX<MAXSTRUCTURES; BX++)
  1383.        if(TidyStringHeap.StructureSegment[BX] == 0) 
  1384.        { TidyStringHeap.StructureSegment[BX] = DX; CL_Found = 1; break; }
  1385.   if(!CL_Found) End(202);  // Too many String Structures
  1386.  }
  1387.  // Initialise all the string pointers
  1388.  for(BX=E2[00]; BX<=E2[02]; BX++) E2[BX] = StringHeapStartAddress + 2; // NULL String Address
  1389.  return DX_Segment;
  1390. }
  1391.  
  1392.  
  1393. void DeleteStructure(CX_Segment)
  1394. {
  1395.  preserve ES, SI;
  1396.  
  1397.  ES = CX_Segment;
  1398.  if(E2[02h] >= E2[00h])  // if(StringLastAddress >= StringFirstAddress)
  1399.  {
  1400.   // If the structure has strings then remove the Structure address from TidyStringHeap
  1401.   for(BX=0; BX<MAXSTRUCTURES; BX++)
  1402.        if(TidyStringHeap.StructureSegment[BX] == CX) 
  1403.        { TidyStringHeap.StructureSegment[BX] = 0; break; }
  1404.   // Delete the strings from the String Heap
  1405.   for(SI=E2[00]; SI<=E2[02]; SI++)
  1406.   {
  1407.    BX = E2[SI];
  1408.    while(S1[BX] != 0) S1[BX++] = 0;
  1409.    E2[SI] = "Undefined";
  1410.   }
  1411.  } 
  1412.  ReleaseMemoryBlock(CX);
  1413. }
  1414.  
  1415.  
  1416. void DisableControlC()
  1417. {
  1418.  preserve DX;
  1419.  
  1420.  AX = ADDRESSOF(IgnoreControlC); // Get address of Interrupt
  1421.  PUSH DS;
  1422.  DX = AX; DS = CS;             // Set up address of interrupt for Control C
  1423.  AH = 25h; AL = 23h; INT 21h;  // Set new interrupt vector for INT 23h
  1424.  POP DS;
  1425. }
  1426.  
  1427.  
  1428. interrupt IgnoreControlC()
  1429. {
  1430.  // When Control C is disabled and a Control C is detected this Interrupt
  1431.  // which does nothing is run every time a Control C is pressed
  1432. }
  1433.  
  1434.  
  1435. void EnableControlC()
  1436. {
  1437.  preserve ES;
  1438.  
  1439.  ES = Start.PSPAddress;
  1440.  PUSH DS;
  1441.  DX = E2[0Eh]; DS = E2[10h];   // Set up origional address of Control C Interrupt
  1442.  AH = 25h; AL = 23h; INT 21h;  // Set new interrupt vector for INT 23h
  1443.  POP DS;
  1444. }
  1445.  
  1446.  
  1447.  
  1448.